// 自定义协议 --- 这个就是协议的定制部分
// 通信双方要通信 , 就要有协议定制 , 让对方都知道彼此怎么通信 ?

#if 0
// 在 TCP 中通信双端中 :  传输层中彼此都会有两个缓冲区 - > 
// 接收缓冲区和发送缓冲区
// 所有信息发过来是拷贝到接受缓冲区中
// 所有信息发过去是拷贝到发送缓冲区中 

// 1 . 为什么要有协议定制呢 ?? 
// 因为 TCP 是面向字节流的 , 每次读取数据可能就会不完整 , 所以我们要自定义协议让其得到完整的数据报发送

// 2. 序列化和反序列化
// 我们定制的协议都是结构体或类 , 所以要发送这个协议数据就要先序列化 , 即 : 转化为字符串同一发送
// 传输层(在内核中)收到的是序列化字符串 , 所以要转成结构体类型让用户使用 , 这个是 : 反序列化
这里使用 :json 串 -- ***********  jsoncpp 库
// 所以 , 要包含头文件 : #include <jsoncpp/json/json.h>
// 链接时要 :  -ljsoncpp --- 动态链接
#endif

// 所以 , 对于客户端来说是什么 ?? 发送请求
// 对于服务端来说是什么 ?? 接收响应
// 所以有两个结构

// 1. 请求  2 . 响应 -- 这两个就是协议 ! 约定好了就这么发
#pragma once

#include <iostream>
#include <string>
#include <functional>

// 序列化和反序列化
#include <jsoncpp/json/json.h> // json 头文件 -- 因为要连接 json库,所以要找到声明

#include "Socket.hpp"
#include "InteInfo.hpp"

using namespace SocketModel;

// client - > server  -- 客户端负责发送要计算的数据
// 客户端请求服务器
class Request
{
public:
    Request(int x, int y, char oper)
        : _x(x), _y(y), _oper(oper)
    {
    }

    Request() {}

    ~Request() {}

    // 但是发送数据途中 , 要进行序列化和反序列化
    // 因为 client 要给 server 发 : 就要先序列化 , 再反序列化

    // 1. 序列化 -- 生成一个完整的字符串
    std::string Serialize()
    {
        // 固定写法
        Json::Value root;
        // 键值添值
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper; // _oper是char，也是整数，阿斯克码值

        // 这个是一个一行风格的字符串
        Json::FastWriter write;                      // 要序列化就是写
        std::string json_string = write.write(root); // 将 root 格式化字符串
        return json_string;
    }

    // 2. 反序列化 -- json串->结构数据
    bool Deserialize(std::string &in)
    {
        // parse , 要反序列化就是要读, 往外所以外面要读
        Json::Value root;
        Json::Reader read;
        bool ok = read.parse(in, root); // 将谁反序列化到 root 中 ?
        if (ok)
        {
            // 反序列成功了 -- 用序列化的数据初始化我
            _x = root["x"].asInt();       // _x 是 Int 整数
            _y = root["y"].asInt();       // _y 是 Int 整数
            _oper = root["oper"].asInt(); // char 也是整数 ,ACSSIC 值
        }
        return ok;
    }

    int GetX() { return _x; }
    int GetY() { return _y; }
    char GetOper() { return _oper; }

private:
    int _x;
    int _y;
    char _oper; // 操作数 -- * + - / % ...
    // 并且这里规定 : 只能 -- > _x _oper _y --- 这就是约定!!!!!!!!!!!!!!!!!!
};

// server -> client  -- 服务器负责发回结果
// 服务器返回结果给客户端
class Response
{
public:
    Response(int result, int code) : _result(result), _code(code)
    {
    }

    Response() {}

    ~Response() {}

    // 1. 序列化 -- 生成一个完整的字符串
    std::string Serialize()
    {
        //// 固定格式 :

        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        // 序列化就是要发 , write
        Json::FastWriter write;
        std::string json_string = write.write(root);
        return json_string;
    }

    // 2. 反序列化 -- json串->结构数据
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        // 发序列化就是要读
        Json::Reader read;
        bool ok = read.parse(in, root); // 什么读到 root 中
        if (ok)
        {
            // 反序列化成功
            // 填充数据
            _result = root["result"].asInt(); // _result 是 Int
            _code = root["code"].asInt();     // _code 是 Int
        }
        return ok;
    }

    // 得到结果设置进来 !
    void SetResult(int res)
    {
        _result = res;
    }
    void SetCode(int code)
    {
        _code = code;
    }
    void ShowResult()
    {
        std::cout << "计算结果是: " << _result << "[" << _code << "]" << std::endl;
    }

private:
    int _result;
    int _code; // 看是否为结果正确
    // 0 为正常
    // 1 为除0错误
    // 2 为 mod 错误
    // .....
};

// 协议 --- 自定义协议  -- 外部就用这个定义协议

#if 0 
// 协议(基于TCP的)需要解决两个问题：
// 1. request和response必须得有序列化和反序列化功能
// 2. 你必须保证，读取的时候，读到完整的请求(TCP, UDP不用考虑)


//所以 , 要设定协议 , 即 : 怎么读 ? 
// 这里手动写一个 , 练习 : 
// 50\r\n""x": 10, "y" : 20, "oper" : '+'"\r\n 
// 50 为 : json串的长度 
**** 所以 , 我们规定协议 : 
// 1. 必须有报头 -- 即 : 表示 json串的长度 
// 2. 有 \r\n 2个
// 3. 50 ~ 最后一个 \r\n 称为一个完成报文 !

**** 所以 , 以上就是协议 , 就是我们约到好了怎么读
#endif

const std::string sep = "\r\n"; // 我们协议定制规定的需要 \r\n

// 这个是处理计算的任务类型
using header_cal_task = std::function<Response(Request &req)>;

class Protocol
{
public:
    Protocol(header_cal_task task)
        : _fun(task)
    {
    }
    Protocol()
    {
    }

    ~Protocol() {}

    // 给一个 json 串添加报头和 \r\n
    std::string Encode(std::string &json_string)
    {
        // 50\r\n" "x": 10, "y" : 20, "oper" : '+' "\r\n
        //  json串长度 + \r\n + json串 + \r\n  -- 作为一个完整报文;
        std::string json_len = std::to_string(json_string.size());
        return json_len + sep + json_string + sep;
    }

    // 获取一个完整报文 !!!!!!!!!!! 即 : 要获取一个完整的报文 : 报头+sep+json串+sep , 然后解析出json
    // 将一个完整的报文进行解码 -- 得到一个完整的报文
    // 因为 TCP 是面向字节流的 , 所以读到的内容 :
    // 可能是 :  50
    //  50\r\n
    // 505\r\n"" ..... 意思就是读到的内容一次可能不完整
    // 所以 , 解码我们必须保证读到的内容必须是一个完整的报文
    // 然后把这个 json 串带出去
    bool DecodeForJson(std::string &buffer, std::string *package)
    {
        // 50\r\n" "x": 10, "y" : 20, "oper" : '+' "\r\n
        // 50\r\n ...

        // 读上来的数据放到了 buffer 中 , 所以在这个里面查找
        auto pos = buffer.find(sep);
        if (pos == std::string::npos)
        {
            // 如果连 \r\n都没有找到,就意味着这次读到的内容无效
            // 50 就是这样的
            return false; // 就要让调用方继续从内核中获取数据继续读
        }

        // 走到这里证明至少有一个 \r\n , 所以可以先获取到json的长度了 , 即 : 报头
        std::string package_str = buffer.substr(0, pos); // 获取报头子串
        // 将报头的子串转为整数 , 报头就是 json 串的长度
        int json_len = std::stoi(package_str);

        // 怎么获取完整报文呢 ??
        //  1. 一个报文的完整长度
        //  50\r\n" "x": 10, "y" : 20, "oper" : '+' "\r\n
        //  报头大小 + 2个(\r\n)分隔符大小 + json 串的大小
        int target_len = package_str.size() + json_len + 2 * sep.size();
        if (buffer.size() < target_len)
        {
            // 证明现在 buffer 中没有一个完整的报文长度 , 返回继续从内核中获取
            return false;
        }

        // 走到这里 , 证明现在一定至少有一个完整报文了
        // 如果有多个完整报文 , 我们每次只获取一个完整报文 , 所以就把获取了的那个删除即可
        // 将一个完整的 json 带出去 , 让外面使用 json 串
        *package = buffer.substr(pos + sep.size(), json_len);

        // 删除这一个完整报文 , 进行下次解析 json串
        buffer.erase(0, target_len);

        return true;
    }

    // 这个是获取请求 , 那对应的谁呀 ?? 服务器 -- 获取客户端发来的请求
    // 获取到请求后 , 我就要处理请求(这里让上层去处理)
    // 获取请求要知道 socket , 和 client 网络信息
    void GetRequest(std::shared_ptr<Socket> &sock, InteInfo &client)
    {
        std::string resbuffer;
        // 获取请求 -- 本质是从接收缓冲区中读取内容
        // 客户端给我发来的是一个 json 串 , 是一个字符串
        while (true)
        {

            int n = sock->Recv(&resbuffer);
            if (n > 0)
            {

                // 但是你给我发的是一个json串 ,我从接收缓冲区中获取不一定获取完整呀 ! 因为是 TCP
                // 进来证明获取到内容了 , 获取到了还要判断获取的内容的完整性 , 是不是一个完整的报文
                // 所以 ,要进行解析内容
                std::string package;                       // 存储报文 -- 即 : 一个完整的 json 串
                while (DecodeForJson(resbuffer, &package)) /*所以 , 一直解析获取, 直到读完*/
                {
                    // 走到这里 , 证明一定拿到了一个完整的报文[这个报文是一个json串]
                    // 拿到了干嘛 ?? 1. 处理 2 . 发回给客户端
                    LOG(LogLevel::DEBUG) << client.InfoAddr() << "获取报文 : " << package;

                    // 0. 我拿到的是字符串 , json 串 , 但是要处理我必须有里面的数据
                    // 1. 反序列化 -- 得到数据
                    Request req;
                    bool ok = req.Deserialize(package);
                    if (!ok)
                        continue;
                    // 2. 拿到了结构化数据 ,  处理任务 - 我该让上层处理了 , 然后给我返回
                    Response result = _fun(req);
                    // 3. 处理完还是结构化数据 , 但我要发给客户端 , 所以要 *** 序列化
                    std::string json_string = result.Serialize();
                    // 4. 此时我们获得了一个 json 串 ,但是协议中我们规定了 : 发回需要包含 : 报头 + sep + json + sep 这样
                    //  所以 , 添加报头等信息
                    std::string packgae_string = Encode(json_string); // 获取完整报文信息发回
                    // 5. 发回给客户端 -- 此时发的是一个序列化的串
                    sock->Send(packgae_string);
                }
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "client:" << client.InfoAddr() << "Quit!";
                break;
            }
            else
            {
                LOG(LogLevel::WARNING) << "client:" << client.InfoAddr() << ", recv error";
                break;
            }
        }
    }

    // 这是收到响应 ?? 谁呀 ?? 客户端 , 客户端收到服务器来回来的结果
    bool GetResponse(std::shared_ptr<Socket> &client, std::string &resp_buff, Response *resp)
    {
        // 客户端收到报文信息 , 会存在其接收缓冲区中 , 然后从该缓冲区中读取
        // 那 client 怎么能保证获取的内容就是完整的呢 ?? 毕竟是 TCP 呀
        // 所以 , 客户端也要解析 !!!!

        while (true)
        {
            // 1. 接收
            int n = client->Recv(&resp_buff);
            if (n > 0)
            {
                // 收到内容了 , 我要解析报文看我现在从接收缓冲区读上来的报文是否完整呀
                // 而发过来我收到的是一个 json 串
                std::string json_package;
                while (DecodeForJson(resp_buff, &json_package)) /*所以 , 一直解析获取, 直到读完*/
                {
                    // 这里证明我读到了一个完整的报文了 , 但是这个报文是一个 json 串
                    // 我要的是结构化数据 , 我要使用的呀 , 所以要 :
                    //  **** 反序列化 ****  -- 反序列化成啥呀 , 结果类 -- 让用户拿到协议定制的内容(Response类)
                    resp->Deserialize(json_package);
                }

                // 走到这里 , 证明我得到了服务端给我发来的结构化报文数据了
                return true;
            }
            else if (n == 0)
            {
                // 服务端退出了
                std::cout << "server quit " << std::endl;
                return false;
            }
            else
            {
                // 接收异常
                std::cout << "recv error" << std::endl;
                return false;
            }
        }
    }

    // 该接口是构建一个请求串 , 将来要给什么请求是客户端用户自己定义的
    // 客户端用户只会这样用 : 10 + 10 , 但是我们要发的完整报文是 : 报头 + sep + json + sep 这样
    // 所以 , 要提供构造这样请求的接口

    std::string BuildRequestString(int x, int y, char oper)
    {
        // 1 . 构建一个请求类
        Request req(x, y, oper);

        // 用户这里首先是结构化信息 , 要添加报文等必须是序列化的

        // 2. 序列化
        std::string json_req = req.Serialize();

        // 3. 添加报文等信息并返回
        return Encode(json_req);
    }

private:
    // 处理计算的任务类型 , 让上层处理
    header_cal_task _fun;
};